*-----------------------------------------------------------
* Program    : CSS 422 Final Project - 68k Disassembler
* Written by : 
* Date       :
* Description:
* 
*-----------------------------------------------------------

********************* EQUate SECTION *************************

stack	EQU	$000A0000	* Bottom of the stack.
lines	EQU	29		* Number of lines to display per screen.
maxlen	EQU	80		* Maximum length for a single string. 

* TRAP #15 tasks
lineout	EQU	00		* Output a line to display with newline
textout	EQU	01		* Output a line to display w/0 newline
keybdin	EQU	02		* Read string from keyboard
				*   String stored at A1
				*   Length stored at D1.W (max 80)
minadd 	EQU	$00000400	* Minimum bound for address entry
maxadd 	EQU	$7FFFFFFF	* Maximum bound for address entry

chmask	EQU	$0000000F	* Mask for I/O

lf	EQU	$A
cr	EQU	$D


true	EQU	1		* For various boolean purposes.
false	EQU	0
				
********************* MAIN PROGRAM ***************************

		ORG	$1000
	
		LEA	stack, SP	* Initialize Stack Pointer
		BRA	welcomeprmpt

reset		MOVE.W	#11, D0
		MOVE.W	#$00FF, D1	* Clear Screen
		TRAP	#15		

		
welcomeprmpt	MOVE.B	#lineout, D0	* Set up println for TRAP #15
		MOVE.W	welcome_lng, D1	* Give TRAP #15 the length of the string.
		LEA	welcome_txt, A1	* Prepare to print welcome message.
		TRAP	#15		* Print Welcome Message.
		
		MOVE.B	#0, D5		* D5 will become line counter.
		
		JSR	getstartaddr    * Prompt for and receive start addr.
		JSR	getendaddr	* Prompt for and receive end address.
		
iterate		ADDQ.B	#1, D6		* Increment counter
		JSR	preop		* Perform pre-opcode tasks.
		JSR	opcode		* Do work, son!
		
* Assumes that opcode will move the program pointer ahead to the next command.
					
		JSR	output		* Output results of opcode

		JSR	postop		* Post output processing.
					* Result in D6
		
		CMP	#3, D5		* Another line
		BEQ	iterate
		
		CMP	#2, D5		* New page of current disassembly.
		BNE	newasmbflag	* If not equal, skip down.
		
					* Do this if more data requested.
		MOVEQ.L	#0, D6		* Reset Counter
		BRA	iterate		* start again.
		
newasmbflag	CMP	#1, D5		* New Disassembly flag given.
		BEQ	reset		* Start over.
		
		CMP	#0, D5		* Quit flag given.
		
exit_program	MOVE.B	#lineout, D0	* set up println for TRAP #15
		MOVE.W	exit_lng, D1	* Give TRAP #15 the length of the string.
		LEA	exit_txt, A1	* Pointer to the string.
		TRAP 	#15		* Print exit message.

	STOP	#$2700

******************** VARIABLES AND STRINGS ********************

start_add	DS.L	1
end_add		DS.L	1
new_num		DS.L	1
mask		DS.L	1

good_buffer	DS.B	maxlen
goodbuffer_lng	DC.B	goodbuffer_lng-good_buffer

bad_buffer	DS.B	maxlen
badbuffer_lng	DC.B	badbuffer_lng-bad_buffer

temp_txt	DS.B	maxlen
temp_lng	DS.B	temp_lng-temp_txt

welcome_txt	DC.B	'Welcome.  Enter Q at any prompt to quit.  (More stuff here)',0
welcome_lng	DC.W	welcome_lng-welcome_txt

start_pmt	DC.B	'Please enter the address to begin decompiling:',0
start_lng	DC.W	start_lng-start_pmt

end_pmt		DC.B	'Please enter the address to end decompiling:',0
end_lng		DC.W	end_lng-end_pmt
		
starterr_txt   	DC.B	'Start address invalid.',0
starterr_lng	DC.W	starterr_lng-starterr_txt

enderr_txt	DC.B	'End address invalid.',0
enderr_lng	DC.W	enderr_lng-enderr_txt

data_txt	DC.B	'DATA: '
data_lng	DC.W	data_lng-data_txt

more_txt	DC.B	'Would you like to see more output? (M)ore, (N)ew Disassembly, (Q)uit',0
more_lng	DC.W	more_lng-more_txt

nextprog_txt	DC.B	'That does it for this one! Shall we do another? (N)ew Disassembly, (Q)uit',0
nextprog_lng	DC.W	nextprog_lng-nextprog_txt

generr_txt	DC.B	'Invalid entry.  Please try again.',0
generr_lng	DC.W	generr_lng-generr_txt

exit_txt	DC.B	'Thanks for a great quarter Arnie.  Enjoy skiing!',0
exit_lng	DC.W	exit_lng-exit_txt

***********************  I/O SUBROUTINES  *********************

*      API for I/O Subroutines
* D0: TRAP 15 Task Number
* D1: Length of display string
* D2: 
* D3: (Internal) For comparison of addresses
* D4: (Internal) For comparison of addresses
* D5:
* D6:
* D7: 

* A0: 
* A1: Address location of input / output strings
* A2:
* A3:
* A4:
* A5:
* A6:
* A7: Stack Pointer.


** getstartaddr Subroutine


	
getstartaddr	MOVEM.L	D3/D4, -(SP)
startoutput	MOVE.B	#textout, D0	* Ensure that println is selected
		MOVE.W	start_lng, D1	* Give TRAP #15 the length of the string.
		LEA	start_pmt, A1	* Prepare to print start address prompt.
		TRAP	#15		* Fire textpedo!
		
start_input	MOVE.B	#keybdin, D0	* Set up keyboard input for TRAP #15
		LEA	temp_txt, A1	* Store the input at temp_txt
		TRAP	#15		* Fire textpedo!
		
		JSR	stringtonum	* Convert the input to a number
		MOVE.L	new_num, start_add	 * Save the new address.
		
		MOVE.L	start_add, D3
		MOVE.L	#minadd+8, D4
		CMP	D3, D4
		BGT	startadderror	* If the start address < minaddress, error.
		
		MOVE.L	#maxadd-8, D4
		CMP	D3, D4
		BGT	startadderror	* If the start address > maxaddress, error.
		
		MOVEM.L	(SP)+, D3/D4
		RTS			* Done.

startadderror	MOVE.B	#lineout, D0	* Ensure that println is selected
		MOVE.W	starterr_lng, D1	* Give TRAP #15 the length of the string.
		LEA	starterr_txt, A1	* Prepare to print start address prompt.
		TRAP	#15		* Fire textpedo!
		
		BRA	startoutput

***********************  I/O SUBROUTINES  *********************

*      API for I/O Subroutines
* D0: TRAP 15 Task Number
* D1: Length of display string
* D2: 
* D3:
* D4:
* D5:
* D6:
* D7: 

* A0: 
* A1: Address location of input / output strings
* A2:
* A3:
* A4:
* A5:
* A6:
* A7: Stack Pointer.


** getendaddr Subroutine ------------------------------------------------------


	
getendaddr	MOVEM.L	D3/D4, -(SP)
		MOVE.B	#textout, D0	* Ensure that println is selected
		MOVE.W	end_lng, D1	* Give TRAP #15 the length of the string.
		LEA	end_pmt, A1	* Prepare to print start address prompt.
		TRAP	#15		* Fire textpedo!
		
end_input	MOVE.B	#keybdin, D0	* Set up keyboard input for TRAP #15
		LEA	temp_txt, A1	* Store the input at temp_txt
		TRAP	#15		* Fire textpedo!
		
		JSR	stringtonum	* Convert the input to a number
		MOVE.L	new_num, end_add * Save the new address.
		
		MOVE.L	end_add, D3	* Compare end address and start bound.
		CMP.L	#minadd, D3
		BLT	endadderror	* If the start address < minaddress, error.
		
		CMP.L	#maxadd, D3		* Compare end address and end bound.
		BGT	endadderror	* If the start address > maxaddress, error.
		
		MOVE.L	start_add+8, D4 * Compare end address and start address.
		CMP.L	D3, D4
		BGT	endadderror	* If the end address < start address, error.
		
		MOVEM.L	(SP)+, D3/D4
		RTS			* Done.

endadderror	MOVE.B	#textout, D0	* Ensure that println is selected
		MOVE.W	enderr_lng, D1	* Give TRAP #15 the length of the string.
		LEA	enderr_txt, A1	* Prepare to print start address prompt.
		TRAP	#15		* Fire textpedo!
		
		BRA	getendaddr
		
		
** stringtonum Subroutine -----------------------------------------------------
** DONE

*      API for stringtonum Subroutine
* D0: (Unused in this subroutine)
* D1: Length of display string
* D2: Output number.  $00000000 if error occured.
* D3: Internal character storage
* D4: Boolean flag, true if on first character
* D5:
* D6:
* D7: 

* A0: 
* A1: Pointer to head of ascii string
* A2:
* A3:
* A4:
* A5:
* A6:
* A7: Stack Pointer.

stringtonum	MOVEM.L	D1/D2/D3/D4/A1,-(SP)	* Push used registers onto stack.
		MOVEQ.L #0, D2		* Erase contents of D2.
		MOVEQ.L	#true, D4	* Set first char flag true.
		
		MOVEQ.L	#0, D3		* Clear D3
		MOVE.B	(A1), D3	* Get first character.
		CMP	#$51, D3	* Check for quit flag.
		BNE	strtonumloop	* If not found, move to loop.
		
		CMP	#1, D1		* Quit flag is only 1 character.
		BEQ	exit_program	* Quit if flag is found and only 1 char.

strtonumloop	MOVEQ.L #0, D3		* Erase previous number.
		MOVE.B	(A1)+, D3	* Get next character.
		
* Possible cases per character, 0-9, A-F, a-f, $ (only valid at beginning):
* '$' = $24,  0-9 = $30-$39,  A-F = $41 - $46, a-f = $61-$66
* All others are invalid for a hexadecimal number.

		CMP	#$24, D3	* Is the character a $?
		BLT	strtonumerr	* If below, it's invalid.
		BNE	numeralcheck	* If !=, skip to next test
		
		CMP	#true, D4	* If >=, is it the first char?
		BEQ	strtonumnext	* If so, skip to next char.
		
numeralcheck	CMP	#$30, D3	* Is the character less than 0?
		BLT	strtonumerr	* If below, it's invalid.
		
		CMP	#$39, D3	* Is the character <= 9?
		BLE	strtonumsave	* If so, it's a valid character.
		
		CMP	#$41, D3	* Is the character less than A?
		BLT	strtonumerr	* If so, it's invalid
		
		CMP	#$46, D3	* Is the character <= F?
		BLE	uppertonumber	* If so, save the value
		
		CMP	#$61, D3	* Is the character < a?
		BLT	strtonumerr	* If so, it's invalid.
		
		CMP	#$66, D3	* Is the character <= f?
		BLE	lowertoupper	* If so, convert to upper case
		BGT	strtonumerr	* If greater than, it's invalid.

strtonumerr	MOVE.L	#$0000, D2	* Save the error value.
		BRA	strtonumexit	* Exit

* 
* FIXME: Below could be optimized further.  At this point, several consecutive
* bit shifts are needed, but the algorithm is the simplest.		

lowertoupper	SUBI.B	#$20, D3	* Subtract from D3 to convert to upper case.

uppertonumber	SUBI.B	#$7, D3		* Subtract from D3 to 'butt up' A against 9

strtonumsave	SUBI.B	#$30, D3	* Subtract from D3 to convert from ASCII to number.
		ASL.L	#4, D2		* Shift D2 left 4 bits, making room for next least
					* significant bit.
		ADD.L	D3, D2		* Add D3 to D2.
		
strtonumnext	SUBQ.B	#1, D1		* Decrement the length counter.
		BGT	strtonumloop	* If characters remain to be written, loop.

		CMP	#1, D3		* Check if the address is an odd boundary
		BEQ	strtonumerr	* Error if it is.
		
strtonumexit	MOVE.L	D2, new_num	 * Save the address to memory.	
		MOVEM.L (SP)+,D1/D2/D3/D4/A1	* Restore previously used registers.
		RTS			* End of subroutine.

** numtostring Subroutine -----------------------------------------------------
* A1: Pointer to string in memory
* D1: Length of string
* D2: Internal Storage
* D3: Mask
* D4: Number for conversion (Input)
* Output and length are saved to memory at temp_txt and temp_lng

numtostring	MOVEM.L	A1/D1-D4, -(SP)	* Push stuff onto stack

		LEA	temp_txt, A1	* Initialize A1.

		MOVE.L	D4, D2		* Copy num into D2 to be counted.
		MOVEQ.L	#0, D1		* Initialize D1

ntscount	CMP	#0, D2		* If D2 has no more data, we're done.
		BEQ	ntscountdone	* D1 has the length now
		
		ASR.L	#4, D2		* Shift D2 left 4 bytes.
		ADDQ.B	#1, D1		* Add 1 to the counter.
		BRA	ntscount	* Go again.		
		
ntscountdone	MOVE.W	D1, temp_lng	* Save the length to memory

		MOVE.B	#$24, (A1)+	* Start with a $ sign.
		MOVE.L	#chmask, D3	* Copy Char mask into reg.
		SUBQ.L	#1, D1		* First decrement
		ASL.L	#2, D1		* Multiply D1 by 4 to use as bit count.
		ASL.L	D1, D3		* Shift D3 by above bit count.
		
ntsloop		MOVE.L	D4, D2		* Copy original number into D2 to work on
		AND.L	D3, D2		* Mask D2
		ASR.L	D1, D2		* Shift D2 to lsb position.

* Now do arithmetic on D2 to convert it to ASCII

		ADD.L	#$30, D2	* Convert to ASCII
		CMP	#$3A, D2	* If 11 or above, add some more to make a letter
		BLE	ntsloopend	* If not, skip
		
		ADD.L	#$7, D2		* Add 7 more to make a capital letter.
		
ntsloopend	SUBQ	#4, D1		* Shift 4 fewer bits next time
		MOVE.B	D2, (A1)+	* Save the character.
		ROR.L	#4, D3		* Shift the mask into the next MSP

		CMP	#0, D1		* Done?
		BNE	ntsloop		* No, do it again.	
ntsdone
		MOVEM.L	(SP)+, A1/D1-D4
		RTS


** preop Subroutine -----------------------------------------------------------
* Purely organizational, this subroutine does not push or pop the stack, as
* several registers must be modified and returned.

preop		NOP

		MOVE.B  false, D7	* Initialize Good flag as false.
		
* Good buffer initialization
		MOVE.W	goodbuffer_lng, D1 * Set up length of good buffer
		LEA	good_buffer, A1	   * Set up pointer to good buffer
		JSR	wipebuffer	   * Wipe the buffer to all spaces.
		LEA	good_buffer, A2	   * Ptr to good buffer lives in A2

* Bad buffer initialization
		MOVE.W	badbuffer_lng, D1 * Set up length of bad buffer
		LEA	bad_buffer, A1	   * Set up pointer to bad buffer
		JSR	wipebuffer	   * Wipe the buffer to all spaces.
		LEA	bad_buffer, A3	   * Ptr to bad buffer lives in A3

* FIXME:  Need to implement string adder		
		MOVE.L  #$44415441,(A3)+	* Add DATA 
		MOVE.W	#$3A20,(A3)+		* Add :_
		
										
exitpreop	RTS


** Wipebuffer Subroutine ------------------------------------------------------
* A1: address of buffer
* D1: Length of buffer

wipebuffer	MOVEM.L	A1/D1, -(SP)	* Put original A1 and D1 on stack
		
wipeloop	CMPI	#0, D1		* Done?
		BEQ	wipedone	* Yes, exit.
		SUBQ.W	#1, D1		* Decrement D1
		MOVE.B	#$20, (A1)+	* Erase A1 and increment.
		BRA	wipeloop	* Go again.
		
wipedone	MOVEM.L (SP)+, A1/D1	* Restore registers.
		RTS
		
		
** output Subroutine ----------------------------------------------------------
* Assumes that work has been done, and either the good/bad buffers have been
* filled, and the good/bad flag is set properly.

output		MOVEM.L A1/D1, -(SP)	* Put stuff on stack

		CMP	#true, D7	* If good flag is true
		BEQ	printgood	* then output the good buffer.
		BRA	printbad	* else, print bad buffer.
		
printgood	LEA	good_buffer, A1	* Load the string and length
		MOVE.W	#goodbuffer_lng, D1
		BRA	outputdone	

printbad	LEA	bad_buffer, A1	* Load the string and length
		MOVE.W	#badbuffer_lng, D1
		BRA	outputdone

outputdone	MOVE.B	#lineout, D0	* Print a line with newline at end.
		TRAP	#15		* GO
		
		MOVEM.L (SP)+, A1/D1	* Restore registers
		RTS
		
** postop Subroutine ----------------------------------------------------------
* Returns value in D5.
* Needs count of lines in D6.

* 0 = Exit program.
* 1 = End current disassembly, start new one.
* 2 = New page of output from current disassembly.
* 3 = Additional line of output from current page of disassembly.

postop		MOVEM.L A1/D0-D2, -(SP) * Push stuff onto stack.
		MOVEQ.L	#-1, D5		* Initialize D6.

		CMP	#lines, D6	* Full screen of info?
		BNE	anotherline	* If not, output another line.

		CMP	#end_add, D0	* Have we reached the end of the program?
		BGE	endofinput	* If so, get input on what to do.

moreoutput	MOVE.B	#lineout, D0	* Output text without line output.
		MOVE.W	#more_lng, D1	* Length of more string
		LEA	more_txt, A1	* More text prompt.
		TRAP	#15		* Go.
		BRA	getinput	* Get input based on prompt.
		
		
endofinput	MOVE.B	#textout, D0	* Output text without line output.
		MOVE.W	#nextprog_lng, D1 * Length of the next program string.
		LEA	nextprog_txt, A1 * Next program string.
		TRAP	#15		* Go.
		

getinput	MOVE.B	#keybdin, D0	* Set up keyboard input for TRAP #15
		LEA	temp_txt, A1	* Store the input at temp_txt
		TRAP	#15		* Fire textpedo!

procinput	MOVE.B	(A1), D2	* Get the first character from input.
					* Ignore the rest.

*(M)ore, (N)ew Disassembly, (Q)uit
* 4D/6D, 4E/6E		
		CMP 	#$4D, D2	* Is it an M?
		BEQ	inputm
		
		CMP	#$6D, D2	* Is it an m?
		BEQ	inputm
		
		CMP	#$4E, D2	* Is it an N?
		BEQ	inputn
		
		CMP	#$6E, D2	* Is it an n?
		BEQ	inputn
		
		CMP	#$51, D2	* Is it a Q?
		BEQ	inputq
		
		CMP	#$71, D2	* Is it a q?
		BEQ	inputq
		
postoperror	MOVE.B	#lineout, D0	* Load and output an error message.
		MOVE.B	generr_lng, D1
		LEA	generr_txt, A1
		TRAP	#15
		BRA	postop
		
inputm		MOVEQ.L	#2, D5		* Save the appropriate flag
		BRA	exitpostop

inputn		MOVEQ.L	#1, D5		* Save the appropriate flag
		BRA	exitpostop

inputq		MOVEQ.L	#0, D5		* Save the appropriate flag
		BRA 	exitpostop

anotherline	MOVEQ.L	#3, D5		* Save flag 3 to D6
exitpostop	MOVEM.L (SP)+, A1/D0-D2 * Pop stuff off of stack.
		RTS

****************************  OP CODE SUBROUTINES *****************************

opcode		NOP

		JSR	ea		* Do EA stuff too.

exitopcode	RTS

*******************************  EA SUBROUTINES  ******************************

ea		NOP

exitea		RTS


	END	$1000












*~Font name~Courier New~
*~Font size~8~
*~Tab type~1~
*~Tab size~8~
